﻿/*
Copyright (c) 2018-2020 Rossmann-Engineering
Permission is hereby granted, free of charge, 
to any person obtaining a copy of this software
and associated documentation files (the "Software"),
to deal in the Software without restriction, 
including without limitation the rights to use, 
copy, modify, merge, publish, distribute, sublicense, 
and/or sell copies of the Software, and to permit 
persons to whom the Software is furnished to do so, 
subject to the following conditions:

The above copyright notice and this permission 
notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE 
OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

***************************************************************************************
李锦上lijinshang@126.com修改版：从机
***************************************************************************************
2023-6-29：删除numberOfClientsChanged、NumberOfConnectedClientsChanged事件
2020-8-13：解决ModbusUDP无法二次启动问题 关闭未结束线程 listenerThread
2020-8-2：增加ReceiveDataChanged(Byte[] data) SendDataChanged(Byte[] data)回调
2020-8-2：解决从机模式接收数据debug信息全部为00的问题
2020-8-1：增加ModbusType 规范化编程
2020-8-1：解决UDP从机模式关闭后不能打开的问题
2020-7-30：解决ModbusRTU从机模式下数据接收错误的问题 详见:SerialHandler -李锦上
***************************************************************************************
*/

using System;
using System.Collections.Generic;
using System.Text;
using System.Net.Sockets;
using System.Net;
using System.Threading;
using System.Net.NetworkInformation;
using System.IO.Ports;

namespace EasyModbus
{
    #region class ModbusProtocol
    /// <summary>
    /// Modbus Protocol informations.
    /// </summary>
    public class ModbusProtocol
    {
        //public enum ProtocolType { ModbusTCP = 0, ModbusUDP = 1, ModbusRTU = 2 };
        public DateTime timeStamp;
        public bool request;
        public bool response;
        public UInt16 transactionIdentifier;
        public UInt16 protocolIdentifier;
        public UInt16 length;
        public byte unitIdentifier;
        public byte functionCode;
        public UInt16 startingAdress;
        public UInt16 startingAddressRead;
        public UInt16 startingAddressWrite;
        public UInt16 quantity;
        public UInt16 quantityRead;
        public UInt16 quantityWrite;
        public byte byteCount;
        public byte exceptionCode;
        public byte errorCode;
        public UInt16[] receiveCoilValues;
        public UInt16[] receiveRegisterValues;
        public Int16[] sendRegisterValues;
        public bool[] sendCoilValues;
        public UInt16 crc;
    }
    #endregion

    #region structs
    struct NetworkConnectionParameter
    {
        public NetworkStream stream;        //For TCP-Connection only
        public Byte[] bytes;
        public int portIn;                  //For UDP-Connection only
        public IPAddress ipAddressIn;       //For UDP-Connection only
    }
    #endregion

    #region TCPHandler class
    internal class TCPHandler
    {
        public delegate void DataChanged(object networkConnectionParameter);
        public event DataChanged dataChanged;

        //public delegate void NumberOfClientsChanged();
        //public event NumberOfClientsChanged numberOfClientsChanged;

        TcpListener server = null;


        private List<Client> tcpClientLastRequestList = new List<Client>();

        public int NumberOfConnectedClients { get; set; }

        public string ipAddress = null;

        /// When making a server TCP listen socket, will listen to this IP address.
        public IPAddress LocalIPAddress
        {
            get { return localIPAddress; }
        }
        private IPAddress localIPAddress = IPAddress.Any;

        /// <summary>
        /// Listen to all network interfaces.
        /// </summary>
        /// <param name="port">TCP port to listen</param>
        public TCPHandler(int port)
        {
            server = new TcpListener(LocalIPAddress, port);
            server.Start();
            server.BeginAcceptTcpClient(AcceptTcpClientCallback, null);
        }

        /// <summary>
        /// Listen to a specific network interface.
        /// </summary>
        /// <param name="localIPAddress">IP address of network interface to listen</param>
        /// <param name="port">TCP port to listen</param>
        public TCPHandler(IPAddress localIPAddress, int port)
        {
            this.localIPAddress = localIPAddress;
            server = new TcpListener(LocalIPAddress, port);
            server.Start();
            server.BeginAcceptTcpClient(AcceptTcpClientCallback, null);
        }


        private void AcceptTcpClientCallback(IAsyncResult asyncResult)
        {
            TcpClient tcpClient = new TcpClient();
            try
            {
                tcpClient = server.EndAcceptTcpClient(asyncResult);
                tcpClient.ReceiveTimeout = 1000;
                if (ipAddress != null)
                {
                    string ipEndpoint = tcpClient.Client.RemoteEndPoint.ToString();
                    ipEndpoint = ipEndpoint.Split(':')[0];
                    if (ipEndpoint != ipAddress)
                    {
                        tcpClient.Client.Disconnect(false);
                        return;
                    }
                }
            }
            catch{ }
            try
            {
                server.BeginAcceptTcpClient(AcceptTcpClientCallback, null);
                Client client = new Client(tcpClient);
                NetworkStream networkStream = client.NetworkStream;
                networkStream.ReadTimeout = 1000;
                networkStream.BeginRead(client.Buffer, 0, client.Buffer.Length, ReadCallback, client);
            }
            catch{ }
        }

        private int GetAndCleanNumberOfConnectedClients(Client client)
        {
            lock (this)
            {
                bool objetExists = false;
                foreach (Client clientLoop in tcpClientLastRequestList)
                {
                    if (client.Equals(clientLoop))
                        objetExists = true;
                }
                try
                {
                    tcpClientLastRequestList.RemoveAll(delegate (Client c)
                    {
                        return ((TimeSpan.FromTicks(DateTime.Now.Ticks - c.Ticks).TotalMilliseconds) > 4000);
                    });
                }
                catch{ }
                if (!objetExists)
                    tcpClientLastRequestList.Add(client);


                return tcpClientLastRequestList.Count;
            }
        }

        private void ReadCallback(IAsyncResult asyncResult)
        {
            NetworkConnectionParameter networkConnectionParameter = new NetworkConnectionParameter();
            Client client = asyncResult.AsyncState as Client;
            client.Ticks = DateTime.Now.Ticks;
            NumberOfConnectedClients = GetAndCleanNumberOfConnectedClients(client);
            //if (numberOfClientsChanged != null)
            //    numberOfClientsChanged();
            if (client != null)
            {
                int read;
                NetworkStream networkStream = null;
                try
                {
                    networkStream = client.NetworkStream;

                    read = networkStream.EndRead(asyncResult);
                }
                catch
                {
                    return;
                }


                if (read == 0)
                {
                    //OnClientDisconnected(client.TcpClient);
                    //connectedClients.Remove(client);
                    return;
                }
                byte[] data = new byte[read];
                Buffer.BlockCopy(client.Buffer, 0, data, 0, read);
                networkConnectionParameter.bytes = data;
                networkConnectionParameter.stream = networkStream;
                if (dataChanged != null)
                    dataChanged(networkConnectionParameter);
                try
                {
                    networkStream.BeginRead(client.Buffer, 0, client.Buffer.Length, ReadCallback, client);
                }
                catch
                {
                }
            }
        }

        public void Disconnect()
        {
            try
            {
                foreach (Client clientLoop in tcpClientLastRequestList)
                {
                    clientLoop.NetworkStream.Close(00);
                }
            }
            catch (Exception) { }
            server.Stop();

        }


        internal class Client
        {
            private readonly TcpClient tcpClient;
            private readonly byte[] buffer;
            public long Ticks { get; set; }

            public Client(TcpClient tcpClient)
            {
                this.tcpClient = tcpClient;
                int bufferSize = tcpClient.ReceiveBufferSize;
                buffer = new byte[bufferSize];
            }

            public TcpClient TcpClient
            {
                get { return tcpClient; }
            }

            public byte[] Buffer
            {
                get { return buffer; }
            }

            public NetworkStream NetworkStream
            {
                get
                {

                    return tcpClient.GetStream();

                }
            }
        }
    }
    #endregion

    /// <summary>
    /// Modbus TCP Server.
    /// </summary>
    public class ModbusClient
    {
        public enum ModbusType { ModbusTCP, ModbusUDP, ModbusRTU };
        private ModbusType modbusType;
        private bool debug = false;
        Int32 port = 502;
        //ModbusProtocol receiveData;
        ModbusProtocol sendData = new ModbusProtocol();
        Byte[] bytes = new Byte[2100];
        //public Int16[] _holdingRegisters = new Int16[65535];
        public HoldingRegisters holdingRegisters;
        public InputRegisters inputRegisters;
        public Coils coils;
        public DiscreteInputs discreteInputs;
        private int numberOfConnections = 0;
        private int baudrate = 9600;
        private System.IO.Ports.Parity parity = Parity.Even;
        private System.IO.Ports.StopBits stopBits = StopBits.One;
        private string serialPort = "COM1";
        private SerialPort serialport;
        private byte unitIdentifier = 1;
        private int portIn;
        private IPAddress ipAddressIn;
        private UdpClient udpClient;
        private IPEndPoint iPEndPoint;
        private TCPHandler tcpHandler;
        private int connectTimeout = 1000;
        Thread listenerThread;
        private ModbusProtocol[] modbusLogData = new ModbusProtocol[100];
        public bool FunctionCode1Disabled { get; set; }
        public bool FunctionCode2Disabled { get; set; }
        public bool FunctionCode3Disabled { get; set; }
        public bool FunctionCode4Disabled { get; set; }
        public bool FunctionCode5Disabled { get; set; }
        public bool FunctionCode6Disabled { get; set; }
        public bool FunctionCode15Disabled { get; set; }
        public bool FunctionCode16Disabled { get; set; }
        public bool FunctionCode23Disabled { get; set; }
        public bool PortChanged { get; set; }
        object lockCoils = new object();
        object lockHoldingRegisters = new object();
        private volatile bool shouldStop;

        private IPAddress localIPAddress = IPAddress.Any;

        public delegate void ReceiveDataChangedHandler(Byte[] data);
        public event ReceiveDataChangedHandler ReceiveDataChanged;
        
        public delegate void SendDataChangedHandler(Byte[] data);
        public event SendDataChangedHandler SendDataChanged;

        /// <summary>
        /// When creating a TCP or UDP socket, the local IP address to attach to.
        /// </summary>
        public IPAddress LocalIPAddress
        {
            get { return localIPAddress; }
            set { if (listenerThread == null) localIPAddress = value; }
        }


        public ModbusClient()
        {
            holdingRegisters = new HoldingRegisters(this);
            inputRegisters = new InputRegisters(this);
            coils = new Coils(this);
            discreteInputs = new DiscreteInputs(this);

        }

        #region events
        public delegate void CoilsChangedHandler(int coil, int numberOfCoils);
        public event CoilsChangedHandler CoilsChanged;

        public delegate void HoldingRegistersChangedHandler(int register, int numberOfRegisters);
        public event HoldingRegistersChangedHandler HoldingRegistersChanged;

        //public delegate void NumberOfConnectedClientsChangedHandler();
        //public event NumberOfConnectedClientsChangedHandler NumberOfConnectedClientsChanged;

        public delegate void LogDataChangedHandler();
        public event LogDataChangedHandler LogDataChanged;
        #endregion

        public void Listen()
        {

            listenerThread = new Thread(ListenerThread);
            listenerThread.Start();
        }

        public void StopListening()
        {
            switch (modbusType)
            {
                case ModbusType.ModbusRTU:

                    if (serialport != null)
                    {
                        if (serialport.IsOpen)
                            serialport.Close();
                        //shouldStop = true;
                    }
                    break;
                case ModbusType.ModbusTCP:
                    try
                    {
                        if (tcpHandler != null)
                            tcpHandler.Disconnect();
                        listenerThread.Abort();

                    }
                    catch (Exception) { }
                    break;
                case ModbusType.ModbusUDP:
                    try
                    {
                        if (udpClient != null)
                            udpClient.Close();
                        listenerThread.Abort();

                    }
                    catch (Exception) { }
                    break;
            }
            shouldStop = true;
            listenerThread.Join();
        }

        private void ListenerThread()
        {
            switch (modbusType)
            {
                case ModbusType.ModbusRTU:
                    if (serialport == null)
                    {
                        if (debug) StoreLogData.Instance.Store("EasyModbus RTU-Server listing for incomming data at Serial Port " + serialPort, System.DateTime.Now);
                        serialport = new SerialPort();
                        serialport.PortName = serialPort;
                        serialport.BaudRate = this.baudrate;
                        serialport.Parity = this.parity;
                        serialport.StopBits = stopBits;
                        serialport.WriteTimeout = connectTimeout;
                        serialport.ReadTimeout = connectTimeout;
                        serialport.ReceivedBytesThreshold = 1;
                        serialport.DataReceived += new SerialDataReceivedEventHandler(DataReceivedHandler);
                        serialport.Open();
                    }
                    break;
                case ModbusType.ModbusTCP:
                    tcpHandler = new TCPHandler(LocalIPAddress, port);
                    if (debug) StoreLogData.Instance.Store($"EasyModbus Server listing for incomming data at Port {port}, local IP {LocalIPAddress}", System.DateTime.Now);
                    tcpHandler.dataChanged += new TCPHandler.DataChanged(ProcessReceivedData);
                    //tcpHandler.numberOfClientsChanged += new TCPHandler.NumberOfClientsChanged(numberOfClientsChanged);
                    break;
                case ModbusType.ModbusUDP:
                    //while (!shouldStop)
                    {
                        IPEndPoint localEndoint = new IPEndPoint(LocalIPAddress, port);
                        udpClient = new UdpClient(localEndoint);
                        if (debug) StoreLogData.Instance.Store($"EasyModbus Server listing for incomming data at Port {port}, local IP {LocalIPAddress}", System.DateTime.Now);
                        udpClient.Client.ReceiveTimeout = connectTimeout;
                        iPEndPoint = new IPEndPoint(IPAddress.Any, port);
                        PortChanged = false;

                        try
                        {
                            bytes = udpClient.Receive(ref iPEndPoint);
                            portIn = iPEndPoint.Port;
                            NetworkConnectionParameter networkConnectionParameter = new NetworkConnectionParameter();
                            networkConnectionParameter.bytes = bytes;
                            ipAddressIn = iPEndPoint.Address;
                            networkConnectionParameter.portIn = portIn;
                            networkConnectionParameter.ipAddressIn = ipAddressIn;
                            ParameterizedThreadStart pts = new ParameterizedThreadStart(this.ProcessReceivedData);
                            Thread processDataThread = new Thread(pts);
                            processDataThread.Start(networkConnectionParameter);
                        }
                        catch (Exception)
                        {
                        }
                    }
                    break;
            }
        }

        #region SerialHandler
        private byte[] readBuffer = new byte[2094];
        //private DateTime lastReceive;
        //private int nextSign = 0;
        private void DataReceivedHandler(object sender,
                        SerialDataReceivedEventArgs e)
        {

            const long ticksWait = TimeSpan.TicksPerMillisecond * 2000;//((40*10000000) / this.baudRate);


            SerialPort sp = (SerialPort)sender;

            readBuffer = new byte[256];
            int numbytes = 0;
            int actualPositionToRead = 0;
            DateTime dateTimeLastRead = DateTime.Now;
            //接收数据
            do
            {
                try
                {
                    dateTimeLastRead = DateTime.Now;
                    while ((sp.BytesToRead) == 0)
                    {
                        System.Threading.Thread.Sleep(1);
                        if ((DateTime.Now.Ticks - dateTimeLastRead.Ticks) > ticksWait)
                            break;
                    }
                    numbytes = sp.BytesToRead;


                    byte[] rxbytearray = new byte[numbytes];
                    sp.Read(rxbytearray, 0, numbytes);
                    Array.Copy(rxbytearray, 0, readBuffer, actualPositionToRead, numbytes);

                    actualPositionToRead = actualPositionToRead + rxbytearray.Length;

                }
                catch (Exception)
                {

                }

                //if (bytesToRead <= actualPositionToRead)
                //    break;

                if (ModbusPoll.DetectValidModbusFrame(readBuffer, actualPositionToRead))
                    break;
            }
            while ((DateTime.Now.Ticks - dateTimeLastRead.Ticks) < ticksWait);

            Byte[] receiveBuffer = new byte[actualPositionToRead];
            Array.Copy(readBuffer, 0, receiveBuffer, 0, actualPositionToRead);//receiveBuffer完整帧数据

            //帧处理
            NetworkConnectionParameter networkConnectionParameter = new NetworkConnectionParameter();
            networkConnectionParameter.bytes = receiveBuffer;
            ParameterizedThreadStart pts = new ParameterizedThreadStart(this.ProcessReceivedData);
            Thread processDataThread = new Thread(pts);
            processDataThread.Start(networkConnectionParameter);
        }
        #endregion

        //#region Method numberOfClientsChanged
        //private void numberOfClientsChanged()
        //{
        //    numberOfConnections = tcpHandler.NumberOfConnectedClients;
        //    if (NumberOfConnectedClientsChanged != null)
        //        NumberOfConnectedClientsChanged();
        //}
        //#endregion

        object lockProcessReceivedData = new object();
        #region Method ProcessReceivedData
        private void ProcessReceivedData(object networkConnectionParameter)
        {
            lock (lockProcessReceivedData)
            {
                Byte[] bytes = new byte[((NetworkConnectionParameter)networkConnectionParameter).bytes.Length];
                NetworkStream stream = ((NetworkConnectionParameter)networkConnectionParameter).stream;
                int portIn = ((NetworkConnectionParameter)networkConnectionParameter).portIn;
                IPAddress ipAddressIn = ((NetworkConnectionParameter)networkConnectionParameter).ipAddressIn;


                Array.Copy(((NetworkConnectionParameter)networkConnectionParameter).bytes, 0, bytes, 0, ((NetworkConnectionParameter)networkConnectionParameter).bytes.Length);
                if (debug) StoreLogData.Instance.Store("Received Data: " + BitConverter.ToString(bytes), System.DateTime.Now);
                if (ReceiveDataChanged != null)
                {
                    ReceiveDataChanged(bytes);
                }

                ModbusProtocol receiveDataThread = new ModbusProtocol();
                ModbusProtocol sendDataThread = new ModbusProtocol();

                try
                {
                    UInt16[] wordData = new UInt16[1];
                    byte[] byteData = new byte[2];
                    receiveDataThread.timeStamp = DateTime.Now;
                    receiveDataThread.request = true;
                    if (modbusType == ModbusType.ModbusTCP || modbusType == ModbusType.ModbusUDP)
                    {
                        //Lese Transaction identifier
                        byteData[1] = bytes[0];
                        byteData[0] = bytes[1];
                        Buffer.BlockCopy(byteData, 0, wordData, 0, 2);
                        receiveDataThread.transactionIdentifier = wordData[0];

                        //Lese Protocol identifier
                        byteData[1] = bytes[2];
                        byteData[0] = bytes[3];
                        Buffer.BlockCopy(byteData, 0, wordData, 0, 2);
                        receiveDataThread.protocolIdentifier = wordData[0];

                        //Lese length
                        byteData[1] = bytes[4];
                        byteData[0] = bytes[5];
                        Buffer.BlockCopy(byteData, 0, wordData, 0, 2);
                        receiveDataThread.length = wordData[0];
                    }

                    //Lese unit identifier
                    receiveDataThread.unitIdentifier = bytes[6 - 6 * Convert.ToInt32(modbusType == ModbusType.ModbusRTU)];
                    //Check UnitIdentifier
                    if ((receiveDataThread.unitIdentifier != this.unitIdentifier) & (receiveDataThread.unitIdentifier != 0))
                        return;

                    // Lese function code
                    receiveDataThread.functionCode = bytes[7 - 6 * Convert.ToInt32(modbusType == ModbusType.ModbusRTU)];

                    // Lese starting address 
                    byteData[1] = bytes[8 - 6 * Convert.ToInt32(modbusType == ModbusType.ModbusRTU)];
                    byteData[0] = bytes[9 - 6 * Convert.ToInt32(modbusType == ModbusType.ModbusRTU)];
                    Buffer.BlockCopy(byteData, 0, wordData, 0, 2);
                    receiveDataThread.startingAdress = wordData[0];

                    if (receiveDataThread.functionCode <= 4)
                    {
                        // Lese quantity
                        byteData[1] = bytes[10 - 6 * Convert.ToInt32(modbusType == ModbusType.ModbusRTU)];
                        byteData[0] = bytes[11 - 6 * Convert.ToInt32(modbusType == ModbusType.ModbusRTU)];
                        Buffer.BlockCopy(byteData, 0, wordData, 0, 2);
                        receiveDataThread.quantity = wordData[0];
                    }
                    if (receiveDataThread.functionCode == 5)
                    {
                        receiveDataThread.receiveCoilValues = new ushort[1];
                        // Lese Value
                        byteData[1] = bytes[10 - 6 * Convert.ToInt32(modbusType == ModbusType.ModbusRTU)];
                        byteData[0] = bytes[11 - 6 * Convert.ToInt32(modbusType == ModbusType.ModbusRTU)];
                        Buffer.BlockCopy(byteData, 0, receiveDataThread.receiveCoilValues, 0, 2);
                    }
                    if (receiveDataThread.functionCode == 6)
                    {
                        receiveDataThread.receiveRegisterValues = new ushort[1];
                        // Lese Value
                        byteData[1] = bytes[10 - 6 * Convert.ToInt32(modbusType == ModbusType.ModbusRTU)];
                        byteData[0] = bytes[11 - 6 * Convert.ToInt32(modbusType == ModbusType.ModbusRTU)];
                        Buffer.BlockCopy(byteData, 0, receiveDataThread.receiveRegisterValues, 0, 2);
                    }
                    if (receiveDataThread.functionCode == 15)
                    {
                        // Lese quantity
                        byteData[1] = bytes[10 - 6 * Convert.ToInt32(modbusType == ModbusType.ModbusRTU)];
                        byteData[0] = bytes[11 - 6 * Convert.ToInt32(modbusType == ModbusType.ModbusRTU)];
                        Buffer.BlockCopy(byteData, 0, wordData, 0, 2);
                        receiveDataThread.quantity = wordData[0];

                        receiveDataThread.byteCount = bytes[12 - 6 * Convert.ToInt32(modbusType == ModbusType.ModbusRTU)];

                        if ((receiveDataThread.byteCount % 2) != 0)
                            receiveDataThread.receiveCoilValues = new ushort[receiveDataThread.byteCount / 2 + 1];
                        else
                            receiveDataThread.receiveCoilValues = new ushort[receiveDataThread.byteCount / 2];
                        // Lese Value
                        Buffer.BlockCopy(bytes, 13 - 6 * Convert.ToInt32(modbusType == ModbusType.ModbusRTU), receiveDataThread.receiveCoilValues, 0, receiveDataThread.byteCount);
                    }
                    if (receiveDataThread.functionCode == 16)
                    {
                        // Lese quantity
                        byteData[1] = bytes[10 - 6 * Convert.ToInt32(modbusType == ModbusType.ModbusRTU)];
                        byteData[0] = bytes[11 - 6 * Convert.ToInt32(modbusType == ModbusType.ModbusRTU)];
                        Buffer.BlockCopy(byteData, 0, wordData, 0, 2);
                        receiveDataThread.quantity = wordData[0];

                        receiveDataThread.byteCount = bytes[12 - 6 * Convert.ToInt32(modbusType == ModbusType.ModbusRTU)];
                        receiveDataThread.receiveRegisterValues = new ushort[receiveDataThread.quantity];
                        for (int i = 0; i < receiveDataThread.quantity; i++)
                        {
                            // Lese Value
                            byteData[1] = bytes[13 + i * 2 - 6 * Convert.ToInt32(modbusType == ModbusType.ModbusRTU)];
                            byteData[0] = bytes[14 + i * 2 - 6 * Convert.ToInt32(modbusType == ModbusType.ModbusRTU)];
                            Buffer.BlockCopy(byteData, 0, receiveDataThread.receiveRegisterValues, i * 2, 2);
                        }

                    }
                    if (receiveDataThread.functionCode == 23)
                    {
                        // Lese starting Address Read
                        byteData[1] = bytes[8 - 6 * Convert.ToInt32(modbusType == ModbusType.ModbusRTU)];
                        byteData[0] = bytes[9 - 6 * Convert.ToInt32(modbusType == ModbusType.ModbusRTU)];
                        Buffer.BlockCopy(byteData, 0, wordData, 0, 2);
                        receiveDataThread.startingAddressRead = wordData[0];
                        // Lese quantity Read
                        byteData[1] = bytes[10 - 6 * Convert.ToInt32(modbusType == ModbusType.ModbusRTU)];
                        byteData[0] = bytes[11 - 6 * Convert.ToInt32(modbusType == ModbusType.ModbusRTU)];
                        Buffer.BlockCopy(byteData, 0, wordData, 0, 2);
                        receiveDataThread.quantityRead = wordData[0];
                        // Lese starting Address Write
                        byteData[1] = bytes[12 - 6 * Convert.ToInt32(modbusType == ModbusType.ModbusRTU)];
                        byteData[0] = bytes[13 - 6 * Convert.ToInt32(modbusType == ModbusType.ModbusRTU)];
                        Buffer.BlockCopy(byteData, 0, wordData, 0, 2);
                        receiveDataThread.startingAddressWrite = wordData[0];
                        // Lese quantity Write
                        byteData[1] = bytes[14 - 6 * Convert.ToInt32(modbusType == ModbusType.ModbusRTU)];
                        byteData[0] = bytes[15 - 6 * Convert.ToInt32(modbusType == ModbusType.ModbusRTU)];
                        Buffer.BlockCopy(byteData, 0, wordData, 0, 2);
                        receiveDataThread.quantityWrite = wordData[0];

                        receiveDataThread.byteCount = bytes[16 - 6 * Convert.ToInt32(modbusType == ModbusType.ModbusRTU)];
                        receiveDataThread.receiveRegisterValues = new ushort[receiveDataThread.quantityWrite];
                        for (int i = 0; i < receiveDataThread.quantityWrite; i++)
                        {
                            // Lese Value
                            byteData[1] = bytes[17 + i * 2 - 6 * Convert.ToInt32(modbusType == ModbusType.ModbusRTU)];
                            byteData[0] = bytes[18 + i * 2 - 6 * Convert.ToInt32(modbusType == ModbusType.ModbusRTU)];
                            Buffer.BlockCopy(byteData, 0, receiveDataThread.receiveRegisterValues, i * 2, 2);
                        }
                    }
                }
                catch{ }
                this.CreateAnswer(receiveDataThread, sendDataThread, stream, portIn, ipAddressIn);
                //this.sendAnswer();
                this.CreateLogData(receiveDataThread, sendDataThread);

                if (LogDataChanged != null)
                    LogDataChanged();
            }
        }
        #endregion

        #region Method CreateAnswer
        private void CreateAnswer(ModbusProtocol receiveData, ModbusProtocol sendData, NetworkStream stream, int portIn, IPAddress ipAddressIn)
        {

            switch (receiveData.functionCode)
            {
                // Read Coils
                case 1:
                    if (!FunctionCode1Disabled)
                        this.ReadCoils(receiveData, sendData, stream, portIn, ipAddressIn);
                    else
                    {
                        sendData.errorCode = (byte)(receiveData.functionCode + 0x80);
                        sendData.exceptionCode = 1;
                        sendException(sendData.errorCode, sendData.exceptionCode, receiveData, sendData, stream, portIn, ipAddressIn);
                    }
                    break;
                // Read Input Registers
                case 2:
                    if (!FunctionCode2Disabled)
                        this.ReadDiscreteInputs(receiveData, sendData, stream, portIn, ipAddressIn);
                    else
                    {
                        sendData.errorCode = (byte)(receiveData.functionCode + 0x80);
                        sendData.exceptionCode = 1;
                        sendException(sendData.errorCode, sendData.exceptionCode, receiveData, sendData, stream, portIn, ipAddressIn);
                    }

                    break;
                // Read Holding Registers
                case 3:
                    if (!FunctionCode3Disabled)
                        this.ReadHoldingRegisters(receiveData, sendData, stream, portIn, ipAddressIn);
                    else
                    {
                        sendData.errorCode = (byte)(receiveData.functionCode + 0x80);
                        sendData.exceptionCode = 1;
                        sendException(sendData.errorCode, sendData.exceptionCode, receiveData, sendData, stream, portIn, ipAddressIn);
                    }

                    break;
                // Read Input Registers
                case 4:
                    if (!FunctionCode4Disabled)
                        this.ReadInputRegisters(receiveData, sendData, stream, portIn, ipAddressIn);
                    else
                    {
                        sendData.errorCode = (byte)(receiveData.functionCode + 0x80);
                        sendData.exceptionCode = 1;
                        sendException(sendData.errorCode, sendData.exceptionCode, receiveData, sendData, stream, portIn, ipAddressIn);
                    }

                    break;
                // Write single coil
                case 5:
                    if (!FunctionCode5Disabled)
                        this.WriteSingleCoil(receiveData, sendData, stream, portIn, ipAddressIn);
                    else
                    {
                        sendData.errorCode = (byte)(receiveData.functionCode + 0x80);
                        sendData.exceptionCode = 1;
                        sendException(sendData.errorCode, sendData.exceptionCode, receiveData, sendData, stream, portIn, ipAddressIn);
                    }

                    break;
                // Write single register
                case 6:
                    if (!FunctionCode6Disabled)
                        this.WriteSingleRegister(receiveData, sendData, stream, portIn, ipAddressIn);
                    else
                    {
                        sendData.errorCode = (byte)(receiveData.functionCode + 0x80);
                        sendData.exceptionCode = 1;
                        sendException(sendData.errorCode, sendData.exceptionCode, receiveData, sendData, stream, portIn, ipAddressIn);
                    }

                    break;
                // Write Multiple coils
                case 15:
                    if (!FunctionCode15Disabled)
                        this.WriteMultipleCoils(receiveData, sendData, stream, portIn, ipAddressIn);
                    else
                    {
                        sendData.errorCode = (byte)(receiveData.functionCode + 0x80);
                        sendData.exceptionCode = 1;
                        sendException(sendData.errorCode, sendData.exceptionCode, receiveData, sendData, stream, portIn, ipAddressIn);
                    }

                    break;
                // Write Multiple registers
                case 16:
                    if (!FunctionCode16Disabled)
                        this.WriteMultipleRegisters(receiveData, sendData, stream, portIn, ipAddressIn);
                    else
                    {
                        sendData.errorCode = (byte)(receiveData.functionCode + 0x80);
                        sendData.exceptionCode = 1;
                        sendException(sendData.errorCode, sendData.exceptionCode, receiveData, sendData, stream, portIn, ipAddressIn);
                    }

                    break;
                // Error: Function Code not supported
                case 23:
                    if (!FunctionCode23Disabled)
                        this.ReadWriteMultipleRegisters(receiveData, sendData, stream, portIn, ipAddressIn);
                    else
                    {
                        sendData.errorCode = (byte)(receiveData.functionCode + 0x80);
                        sendData.exceptionCode = 1;
                        sendException(sendData.errorCode, sendData.exceptionCode, receiveData, sendData, stream, portIn, ipAddressIn);
                    }

                    break;
                // Error: Function Code not supported
                default:
                    sendData.errorCode = (byte)(receiveData.functionCode + 0x80);
                    sendData.exceptionCode = 1;
                    sendException(sendData.errorCode, sendData.exceptionCode, receiveData, sendData, stream, portIn, ipAddressIn);
                    break;
            }
            sendData.timeStamp = DateTime.Now;
        }
        #endregion

        private void ReadCoils(ModbusProtocol receiveData, ModbusProtocol sendData, NetworkStream stream, int portIn, IPAddress ipAddressIn)
        {
            sendData.response = true;

            sendData.transactionIdentifier = receiveData.transactionIdentifier;
            sendData.protocolIdentifier = receiveData.protocolIdentifier;

            sendData.unitIdentifier = this.unitIdentifier;
            sendData.functionCode = receiveData.functionCode;
            if ((receiveData.quantity < 1) | (receiveData.quantity > 0x07D0))  //Invalid quantity
            {
                sendData.errorCode = (byte)(receiveData.functionCode + 0x80);
                sendData.exceptionCode = 3;
            }
            if (((receiveData.startingAdress + receiveData.quantity) > 65535) | (receiveData.startingAdress < 0))     //Invalid Starting adress or Starting address + quantity
            {
                sendData.errorCode = (byte)(receiveData.functionCode + 0x80);
                sendData.exceptionCode = 2;
            }
            if (sendData.exceptionCode == 0)
            {
                if ((receiveData.quantity % 8) == 0)
                    sendData.byteCount = (byte)(receiveData.quantity / 8);
                else
                    sendData.byteCount = (byte)(receiveData.quantity / 8 + 1);

                sendData.sendCoilValues = new bool[receiveData.quantity];
                lock (lockCoils)
                    Array.Copy(coils.localArray, receiveData.startingAdress, sendData.sendCoilValues, 0, receiveData.quantity);
            }
            if (true)
            {
                Byte[] data;

                if (sendData.exceptionCode > 0)
                    data = new byte[9 + 2 * Convert.ToInt32(modbusType == ModbusType.ModbusRTU)];
                else
                    data = new byte[9 + sendData.byteCount + 2 * Convert.ToInt32(modbusType == ModbusType.ModbusRTU)];

                Byte[] byteData = new byte[2];

                sendData.length = (byte)(data.Length - 6);

                //Send Transaction identifier
                byteData = BitConverter.GetBytes((int)sendData.transactionIdentifier);
                data[0] = byteData[1];
                data[1] = byteData[0];

                //Send Protocol identifier
                byteData = BitConverter.GetBytes((int)sendData.protocolIdentifier);
                data[2] = byteData[1];
                data[3] = byteData[0];

                //Send length
                byteData = BitConverter.GetBytes((int)sendData.length);
                data[4] = byteData[1];
                data[5] = byteData[0];
                //Unit Identifier
                data[6] = sendData.unitIdentifier;

                //Function Code
                data[7] = sendData.functionCode;

                //ByteCount
                data[8] = sendData.byteCount;

                if (sendData.exceptionCode > 0)
                {
                    data[7] = sendData.errorCode;
                    data[8] = sendData.exceptionCode;
                    sendData.sendCoilValues = null;
                }

                if (sendData.sendCoilValues != null)
                    for (int i = 0; i < (sendData.byteCount); i++)
                    {
                        byteData = new byte[2];
                        for (int j = 0; j < 8; j++)
                        {

                            byte boolValue;
                            if (sendData.sendCoilValues[i * 8 + j] == true)
                                boolValue = 1;
                            else
                                boolValue = 0;
                            byteData[1] = (byte)((byteData[1]) | (boolValue << j));
                            if ((i * 8 + j + 1) >= sendData.sendCoilValues.Length)
                                break;
                        }
                        data[9 + i] = byteData[1];
                    }
                try
                {
                    switch (modbusType)
                    {
                        case ModbusType.ModbusRTU:
                            {
                                if (!serialport.IsOpen)
                                    throw new EasyModbus.Exceptions.SerialPortNotOpenedException("serial port not opened");
                                //Create CRC
                                sendData.crc = ModbusPoll.calculateCRC(data, Convert.ToUInt16(data.Length - 8), 6);
                                byteData = BitConverter.GetBytes((int)sendData.crc);
                                data[data.Length - 2] = byteData[0];
                                data[data.Length - 1] = byteData[1];
                                serialport.Write(data, 6, data.Length - 6);

                                byte[] debugData = new byte[data.Length - 6];
                                Array.Copy(data, 6, debugData, 0, data.Length - 6);
                                if (debug) StoreLogData.Instance.Store("Send Serial-Data: " + BitConverter.ToString(debugData), System.DateTime.Now);
                                if (SendDataChanged != null)
                                {
                                    SendDataChanged(debugData);
                                }
                            }
                            break;
                        case ModbusType.ModbusUDP:
                            {
                                //UdpClient udpClient = new UdpClient();
                                IPEndPoint endPoint = new IPEndPoint(ipAddressIn, portIn);
                                if (debug) StoreLogData.Instance.Store("Send Data: " + BitConverter.ToString(data), System.DateTime.Now);
                                udpClient.Send(data, data.Length, endPoint);
                                if (SendDataChanged != null)
                                {
                                    SendDataChanged(data);
                                }

                            }
                            break;
                        case ModbusType.ModbusTCP:
                            {
                                stream.Write(data, 0, data.Length);
                                if (debug) StoreLogData.Instance.Store("Send Data: " + BitConverter.ToString(data), System.DateTime.Now);
                                if (SendDataChanged != null)
                                {
                                    SendDataChanged(data);
                                }
                            }
                            break;
                    }
                }
                catch (Exception) { }
            }
        }

        private void ReadDiscreteInputs(ModbusProtocol receiveData, ModbusProtocol sendData, NetworkStream stream, int portIn, IPAddress ipAddressIn)
        {
            sendData.response = true;

            sendData.transactionIdentifier = receiveData.transactionIdentifier;
            sendData.protocolIdentifier = receiveData.protocolIdentifier;

            sendData.unitIdentifier = this.unitIdentifier;
            sendData.functionCode = receiveData.functionCode;
            if ((receiveData.quantity < 1) | (receiveData.quantity > 0x07D0))  //Invalid quantity
            {
                sendData.errorCode = (byte)(receiveData.functionCode + 0x80);
                sendData.exceptionCode = 3;
            }
            if (((receiveData.startingAdress + receiveData.quantity) > 65535) | (receiveData.startingAdress < 0))   //Invalid Starting adress or Starting address + quantity
            {
                sendData.errorCode = (byte)(receiveData.functionCode + 0x80);
                sendData.exceptionCode = 2;
            }
            if (sendData.exceptionCode == 0)
            {
                if ((receiveData.quantity % 8) == 0)
                    sendData.byteCount = (byte)(receiveData.quantity / 8);
                else
                    sendData.byteCount = (byte)(receiveData.quantity / 8 + 1);

                sendData.sendCoilValues = new bool[receiveData.quantity];
                Array.Copy(discreteInputs.localArray, receiveData.startingAdress, sendData.sendCoilValues, 0, receiveData.quantity);
            }
            if (true)
            {
                Byte[] data;
                if (sendData.exceptionCode > 0)
                    data = new byte[9 + 2 * Convert.ToInt32(modbusType == ModbusType.ModbusRTU)];
                else
                    data = new byte[9 + sendData.byteCount + 2 * Convert.ToInt32(modbusType == ModbusType.ModbusRTU)];
                Byte[] byteData = new byte[2];
                sendData.length = (byte)(data.Length - 6);

                //Send Transaction identifier
                byteData = BitConverter.GetBytes((int)sendData.transactionIdentifier);
                data[0] = byteData[1];
                data[1] = byteData[0];

                //Send Protocol identifier
                byteData = BitConverter.GetBytes((int)sendData.protocolIdentifier);
                data[2] = byteData[1];
                data[3] = byteData[0];

                //Send length
                byteData = BitConverter.GetBytes((int)sendData.length);
                data[4] = byteData[1];
                data[5] = byteData[0];

                //Unit Identifier
                data[6] = sendData.unitIdentifier;

                //Function Code
                data[7] = sendData.functionCode;

                //ByteCount
                data[8] = sendData.byteCount;


                if (sendData.exceptionCode > 0)
                {
                    data[7] = sendData.errorCode;
                    data[8] = sendData.exceptionCode;
                    sendData.sendCoilValues = null;
                }

                if (sendData.sendCoilValues != null)
                    for (int i = 0; i < (sendData.byteCount); i++)
                    {
                        byteData = new byte[2];
                        for (int j = 0; j < 8; j++)
                        {

                            byte boolValue;
                            if (sendData.sendCoilValues[i * 8 + j] == true)
                                boolValue = 1;
                            else
                                boolValue = 0;
                            byteData[1] = (byte)((byteData[1]) | (boolValue << j));
                            if ((i * 8 + j + 1) >= sendData.sendCoilValues.Length)
                                break;
                        }
                        data[9 + i] = byteData[1];
                    }

                try
                {
                    switch (modbusType)
                    {
                        case ModbusType.ModbusRTU:
                            {
                                if (!serialport.IsOpen)
                                    throw new EasyModbus.Exceptions.SerialPortNotOpenedException("serial port not opened");
                                //Create CRC
                                sendData.crc = ModbusPoll.calculateCRC(data, Convert.ToUInt16(data.Length - 8), 6);
                                byteData = BitConverter.GetBytes((int)sendData.crc);
                                data[data.Length - 2] = byteData[0];
                                data[data.Length - 1] = byteData[1];
                                serialport.Write(data, 6, data.Length - 6);

                                byte[] debugData = new byte[data.Length - 6];
                                Array.Copy(data, 6, debugData, 0, data.Length - 6);
                                if (debug) StoreLogData.Instance.Store("Send Serial-Data: " + BitConverter.ToString(debugData), System.DateTime.Now);
                                if (SendDataChanged != null)
                                {
                                    SendDataChanged(debugData);
                                }
                            }
                            break;
                        case ModbusType.ModbusUDP:
                            {
                                //UdpClient udpClient = new UdpClient();
                                IPEndPoint endPoint = new IPEndPoint(ipAddressIn, portIn);
                                udpClient.Send(data, data.Length, endPoint);
                                if (SendDataChanged != null)
                                {
                                    SendDataChanged(data);
                                }

                            }
                            break;
                        case ModbusType.ModbusTCP:
                            {
                                stream.Write(data, 0, data.Length);
                                if (debug) StoreLogData.Instance.Store("Send Data: " + BitConverter.ToString(data), System.DateTime.Now);
                                if (SendDataChanged != null)
                                {
                                    SendDataChanged(data);
                                }
                            }
                            break;
                    }
                }
                catch (Exception) { }
            }
        }

        private void ReadHoldingRegisters(ModbusProtocol receiveData, ModbusProtocol sendData, NetworkStream stream, int portIn, IPAddress ipAddressIn)
        {
            sendData.response = true;

            sendData.transactionIdentifier = receiveData.transactionIdentifier;
            sendData.protocolIdentifier = receiveData.protocolIdentifier;

            sendData.unitIdentifier = this.unitIdentifier;
            sendData.functionCode = receiveData.functionCode;
            if ((receiveData.quantity < 1) | (receiveData.quantity > 0x007D))  //Invalid quantity
            {
                sendData.errorCode = (byte)(receiveData.functionCode + 0x80);
                sendData.exceptionCode = 3;
            }
            if (((receiveData.startingAdress + receiveData.quantity) > 65535) | (receiveData.startingAdress < 0))   //Invalid Starting adress or Starting address + quantity
            {
                sendData.errorCode = (byte)(receiveData.functionCode + 0x80);
                sendData.exceptionCode = 2;
            }
            if (sendData.exceptionCode == 0)
            {
                sendData.byteCount = (byte)(2 * receiveData.quantity);
                sendData.sendRegisterValues = new Int16[receiveData.quantity];
                lock (lockHoldingRegisters)
                    Buffer.BlockCopy(holdingRegisters.localArray, receiveData.startingAdress * 2, sendData.sendRegisterValues, 0, receiveData.quantity * 2);
            }
            if (sendData.exceptionCode > 0)
                sendData.length = 0x03;
            else
                sendData.length = (ushort)(0x03 + sendData.byteCount);

            if (true)
            {
                Byte[] data;
                if (sendData.exceptionCode > 0)
                    data = new byte[9 + 2 * Convert.ToInt32(modbusType == ModbusType.ModbusRTU)];
                else
                    data = new byte[9 + sendData.byteCount + 2 * Convert.ToInt32(modbusType == ModbusType.ModbusRTU)];
                Byte[] byteData = new byte[2];
                sendData.length = (byte)(data.Length - 6);

                //Send Transaction identifier
                byteData = BitConverter.GetBytes((int)sendData.transactionIdentifier);
                data[0] = byteData[1];
                data[1] = byteData[0];

                //Send Protocol identifier
                byteData = BitConverter.GetBytes((int)sendData.protocolIdentifier);
                data[2] = byteData[1];
                data[3] = byteData[0];

                //Send length
                byteData = BitConverter.GetBytes((int)sendData.length);
                data[4] = byteData[1];
                data[5] = byteData[0];

                //Unit Identifier
                data[6] = sendData.unitIdentifier;

                //Function Code
                data[7] = sendData.functionCode;

                //ByteCount
                data[8] = sendData.byteCount;

                if (sendData.exceptionCode > 0)
                {
                    data[7] = sendData.errorCode;
                    data[8] = sendData.exceptionCode;
                    sendData.sendRegisterValues = null;
                }


                if (sendData.sendRegisterValues != null)
                    for (int i = 0; i < (sendData.byteCount / 2); i++)
                    {
                        byteData = BitConverter.GetBytes((Int16)sendData.sendRegisterValues[i]);
                        data[9 + i * 2] = byteData[1];
                        data[10 + i * 2] = byteData[0];
                    }
                try
                {
                    switch (modbusType)
                    {
                        case ModbusType.ModbusRTU:
                            {
                                if (!serialport.IsOpen)
                                    throw new EasyModbus.Exceptions.SerialPortNotOpenedException("serial port not opened");
                                //Create CRC
                                sendData.crc = ModbusPoll.calculateCRC(data, Convert.ToUInt16(data.Length - 8), 6);
                                byteData = BitConverter.GetBytes((int)sendData.crc);
                                data[data.Length - 2] = byteData[0];
                                data[data.Length - 1] = byteData[1];
                                serialport.Write(data, 6, data.Length - 6);

                                byte[] debugData = new byte[data.Length - 6];
                                Array.Copy(data, 6, debugData, 0, data.Length - 6);
                                if (debug) StoreLogData.Instance.Store("Send Serial-Data: " + BitConverter.ToString(debugData), System.DateTime.Now);
                                if (SendDataChanged != null)
                                {
                                    SendDataChanged(debugData);
                                }
                            }
                            break;
                        case ModbusType.ModbusUDP:
                            {
                                //UdpClient udpClient = new UdpClient();
                                IPEndPoint endPoint = new IPEndPoint(ipAddressIn, portIn);
                                udpClient.Send(data, data.Length, endPoint);
                                if (SendDataChanged != null)
                                {
                                    SendDataChanged(data);
                                }

                            }
                            break;
                        case ModbusType.ModbusTCP:
                            {
                                stream.Write(data, 0, data.Length);
                                if (debug) StoreLogData.Instance.Store("Send Data: " + BitConverter.ToString(data), System.DateTime.Now);
                                if (SendDataChanged != null)
                                {
                                    SendDataChanged(data);
                                }
                            }
                            break;
                    }
                }
                catch (Exception) { }
            }
        }

        private void ReadInputRegisters(ModbusProtocol receiveData, ModbusProtocol sendData, NetworkStream stream, int portIn, IPAddress ipAddressIn)
        {
            sendData.response = true;

            sendData.transactionIdentifier = receiveData.transactionIdentifier;
            sendData.protocolIdentifier = receiveData.protocolIdentifier;

            sendData.unitIdentifier = this.unitIdentifier;
            sendData.functionCode = receiveData.functionCode;
            if ((receiveData.quantity < 1) | (receiveData.quantity > 0x007D))  //Invalid quantity
            {
                sendData.errorCode = (byte)(receiveData.functionCode + 0x80);
                sendData.exceptionCode = 3;
            }
            if (((receiveData.startingAdress + receiveData.quantity) > 65535) | (receiveData.startingAdress < 0))   //Invalid Starting adress or Starting address + quantity
            {
                sendData.errorCode = (byte)(receiveData.functionCode + 0x80);
                sendData.exceptionCode = 2;
            }
            if (sendData.exceptionCode == 0)
            {
                sendData.byteCount = (byte)(2 * receiveData.quantity);
                sendData.sendRegisterValues = new Int16[receiveData.quantity];
                Buffer.BlockCopy(inputRegisters.localArray, receiveData.startingAdress * 2 + 2, sendData.sendRegisterValues, 0, receiveData.quantity * 2);
            }
            if (sendData.exceptionCode > 0)
                sendData.length = 0x03;
            else
                sendData.length = (ushort)(0x03 + sendData.byteCount);

            if (true)
            {
                Byte[] data;
                if (sendData.exceptionCode > 0)
                    data = new byte[9 + 2 * Convert.ToInt32(modbusType == ModbusType.ModbusRTU)];
                else
                    data = new byte[9 + sendData.byteCount + 2 * Convert.ToInt32(modbusType == ModbusType.ModbusRTU)];
                Byte[] byteData = new byte[2];
                sendData.length = (byte)(data.Length - 6);

                //Send Transaction identifier
                byteData = BitConverter.GetBytes((int)sendData.transactionIdentifier);
                data[0] = byteData[1];
                data[1] = byteData[0];

                //Send Protocol identifier
                byteData = BitConverter.GetBytes((int)sendData.protocolIdentifier);
                data[2] = byteData[1];
                data[3] = byteData[0];

                //Send length
                byteData = BitConverter.GetBytes((int)sendData.length);
                data[4] = byteData[1];
                data[5] = byteData[0];

                //Unit Identifier
                data[6] = sendData.unitIdentifier;

                //Function Code
                data[7] = sendData.functionCode;

                //ByteCount
                data[8] = sendData.byteCount;


                if (sendData.exceptionCode > 0)
                {
                    data[7] = sendData.errorCode;
                    data[8] = sendData.exceptionCode;
                    sendData.sendRegisterValues = null;
                }


                if (sendData.sendRegisterValues != null)
                    for (int i = 0; i < (sendData.byteCount / 2); i++)
                    {
                        byteData = BitConverter.GetBytes((Int16)sendData.sendRegisterValues[i]);
                        data[9 + i * 2] = byteData[1];
                        data[10 + i * 2] = byteData[0];
                    }
                try
                {
                    switch (modbusType)
                    {
                        case ModbusType.ModbusRTU:
                            {
                                if (!serialport.IsOpen)
                                    throw new EasyModbus.Exceptions.SerialPortNotOpenedException("serial port not opened");
                                //Create CRC
                                sendData.crc = ModbusPoll.calculateCRC(data, Convert.ToUInt16(data.Length - 8), 6);
                                byteData = BitConverter.GetBytes((int)sendData.crc);
                                data[data.Length - 2] = byteData[0];
                                data[data.Length - 1] = byteData[1];
                                serialport.Write(data, 6, data.Length - 6);

                                byte[] debugData = new byte[data.Length - 6];
                                Array.Copy(data, 6, debugData, 0, data.Length - 6);
                                if (debug) StoreLogData.Instance.Store("Send Serial-Data: " + BitConverter.ToString(debugData), System.DateTime.Now);
                                if (SendDataChanged != null)
                                {
                                    SendDataChanged(debugData);
                                }

                            }
                            break;
                        case ModbusType.ModbusUDP:
                            {
                                //UdpClient udpClient = new UdpClient();
                                IPEndPoint endPoint = new IPEndPoint(ipAddressIn, portIn);
                                udpClient.Send(data, data.Length, endPoint);
                                if (SendDataChanged != null)
                                {
                                    SendDataChanged(data);
                                }

                            }
                            break;
                        case ModbusType.ModbusTCP:
                            {
                                stream.Write(data, 0, data.Length);
                                if (debug) StoreLogData.Instance.Store("Send Data: " + BitConverter.ToString(data), System.DateTime.Now);
                                if (SendDataChanged != null)
                                {
                                    SendDataChanged(data);
                                }
                            }
                            break;
                    }
                }
                catch (Exception) { }
            }
        }

        private void WriteSingleCoil(ModbusProtocol receiveData, ModbusProtocol sendData, NetworkStream stream, int portIn, IPAddress ipAddressIn)
        {
            sendData.response = true;

            sendData.transactionIdentifier = receiveData.transactionIdentifier;
            sendData.protocolIdentifier = receiveData.protocolIdentifier;

            sendData.unitIdentifier = this.unitIdentifier;
            sendData.functionCode = receiveData.functionCode;
            sendData.startingAdress = receiveData.startingAdress;
            sendData.receiveCoilValues = receiveData.receiveCoilValues;
            if ((receiveData.receiveCoilValues[0] != 0x0000) & (receiveData.receiveCoilValues[0] != 0xFF00))  //Invalid Value
            {
                sendData.errorCode = (byte)(receiveData.functionCode + 0x80);
                sendData.exceptionCode = 3;
            }
            if (((receiveData.startingAdress) > 65535) | (receiveData.startingAdress < 0))    //Invalid Starting adress or Starting address + quantity
            {
                sendData.errorCode = (byte)(receiveData.functionCode + 0x80);
                sendData.exceptionCode = 2;
            }
            if (sendData.exceptionCode == 0)
            {
                if (receiveData.receiveCoilValues[0] == 0xFF00)
                {
                    lock (lockCoils)
                        coils[receiveData.startingAdress] = true;
                }
                if (receiveData.receiveCoilValues[0] == 0x0000)
                {
                    lock (lockCoils)
                        coils[receiveData.startingAdress] = false;
                }
            }
            if (sendData.exceptionCode > 0)
                sendData.length = 0x03;
            else
                sendData.length = 0x06;

            if (true)
            {
                Byte[] data;
                if (sendData.exceptionCode > 0)
                    data = new byte[9 + 2 * Convert.ToInt32(modbusType == ModbusType.ModbusRTU)];
                else
                    data = new byte[12 + 2 * Convert.ToInt32(modbusType == ModbusType.ModbusRTU)];

                Byte[] byteData = new byte[2];
                sendData.length = (byte)(data.Length - 6);

                //Send Transaction identifier
                byteData = BitConverter.GetBytes((int)sendData.transactionIdentifier);
                data[0] = byteData[1];
                data[1] = byteData[0];

                //Send Protocol identifier
                byteData = BitConverter.GetBytes((int)sendData.protocolIdentifier);
                data[2] = byteData[1];
                data[3] = byteData[0];

                //Send length
                byteData = BitConverter.GetBytes((int)sendData.length);
                data[4] = byteData[1];
                data[5] = byteData[0];

                //Unit Identifier
                data[6] = sendData.unitIdentifier;

                //Function Code
                data[7] = sendData.functionCode;



                if (sendData.exceptionCode > 0)
                {
                    data[7] = sendData.errorCode;
                    data[8] = sendData.exceptionCode;
                    sendData.sendRegisterValues = null;
                }
                else
                {
                    byteData = BitConverter.GetBytes((int)receiveData.startingAdress);
                    data[8] = byteData[1];
                    data[9] = byteData[0];
                    byteData = BitConverter.GetBytes((int)receiveData.receiveCoilValues[0]);
                    data[10] = byteData[1];
                    data[11] = byteData[0];
                }


                try
                {
                    switch (modbusType)
                    {
                        case ModbusType.ModbusRTU:
                            {
                                if (!serialport.IsOpen)
                                    throw new EasyModbus.Exceptions.SerialPortNotOpenedException("serial port not opened");
                                //Create CRC
                                sendData.crc = ModbusPoll.calculateCRC(data, Convert.ToUInt16(data.Length - 8), 6);
                                byteData = BitConverter.GetBytes((int)sendData.crc);
                                data[data.Length - 2] = byteData[0];
                                data[data.Length - 1] = byteData[1];
                                serialport.Write(data, 6, data.Length - 6);

                                byte[] debugData = new byte[data.Length - 6];
                                Array.Copy(data, 6, debugData, 0, data.Length - 6);
                                if (debug) StoreLogData.Instance.Store("Send Serial-Data: " + BitConverter.ToString(debugData), System.DateTime.Now);
                                if (SendDataChanged != null)
                                {
                                    SendDataChanged(debugData);
                                }

                            }
                            break;
                        case ModbusType.ModbusUDP:
                            {
                                //UdpClient udpClient = new UdpClient();
                                IPEndPoint endPoint = new IPEndPoint(ipAddressIn, portIn);
                                udpClient.Send(data, data.Length, endPoint);
                                if (SendDataChanged != null)
                                {
                                    SendDataChanged(data);
                                }

                            }
                            break;
                        case ModbusType.ModbusTCP:
                            {
                                stream.Write(data, 0, data.Length);
                                if (debug) StoreLogData.Instance.Store("Send Data: " + BitConverter.ToString(data), System.DateTime.Now);
                                if (SendDataChanged != null)
                                {
                                    SendDataChanged(data);
                                }
                            }
                            break;
                    }
                }
                catch (Exception) { }
                if (CoilsChanged != null)
                    CoilsChanged(receiveData.startingAdress, 1);
            }
        }

        private void WriteSingleRegister(ModbusProtocol receiveData, ModbusProtocol sendData, NetworkStream stream, int portIn, IPAddress ipAddressIn)
        {
            sendData.response = true;

            sendData.transactionIdentifier = receiveData.transactionIdentifier;
            sendData.protocolIdentifier = receiveData.protocolIdentifier;

            sendData.unitIdentifier = this.unitIdentifier;
            sendData.functionCode = receiveData.functionCode;
            sendData.startingAdress = receiveData.startingAdress;
            sendData.receiveRegisterValues = receiveData.receiveRegisterValues;

            if ((receiveData.receiveRegisterValues[0] < 0x0000) | (receiveData.receiveRegisterValues[0] > 0xFFFF))  //Invalid Value
            {
                sendData.errorCode = (byte)(receiveData.functionCode + 0x80);
                sendData.exceptionCode = 3;
            }
            if (((receiveData.startingAdress + 1) > 65535) | (receiveData.startingAdress < 0))    //Invalid Starting adress or Starting address + quantity
            {
                sendData.errorCode = (byte)(receiveData.functionCode + 0x80);
                sendData.exceptionCode = 2;
            }
            if (sendData.exceptionCode == 0)
            {
                lock (lockHoldingRegisters)
                    holdingRegisters[receiveData.startingAdress] = unchecked((short)receiveData.receiveRegisterValues[0]);
            }
            if (sendData.exceptionCode > 0)
                sendData.length = 0x03;
            else
                sendData.length = 0x06;

            if (true)
            {
                Byte[] data;
                if (sendData.exceptionCode > 0)
                    data = new byte[9 + 2 * Convert.ToInt32(modbusType == ModbusType.ModbusRTU)];
                else
                    data = new byte[12 + 2 * Convert.ToInt32(modbusType == ModbusType.ModbusRTU)];

                Byte[] byteData = new byte[2];
                sendData.length = (byte)(data.Length - 6);


                //Send Transaction identifier
                byteData = BitConverter.GetBytes((int)sendData.transactionIdentifier);
                data[0] = byteData[1];
                data[1] = byteData[0];

                //Send Protocol identifier
                byteData = BitConverter.GetBytes((int)sendData.protocolIdentifier);
                data[2] = byteData[1];
                data[3] = byteData[0];

                //Send length
                byteData = BitConverter.GetBytes((int)sendData.length);
                data[4] = byteData[1];
                data[5] = byteData[0];

                //Unit Identifier
                data[6] = sendData.unitIdentifier;

                //Function Code
                data[7] = sendData.functionCode;



                if (sendData.exceptionCode > 0)
                {
                    data[7] = sendData.errorCode;
                    data[8] = sendData.exceptionCode;
                    sendData.sendRegisterValues = null;
                }
                else
                {
                    byteData = BitConverter.GetBytes((int)receiveData.startingAdress);
                    data[8] = byteData[1];
                    data[9] = byteData[0];
                    byteData = BitConverter.GetBytes((int)receiveData.receiveRegisterValues[0]);
                    data[10] = byteData[1];
                    data[11] = byteData[0];
                }


                try
                {
                    switch (modbusType)
                    {
                        case ModbusType.ModbusRTU:
                            {
                                if (!serialport.IsOpen)
                                    throw new EasyModbus.Exceptions.SerialPortNotOpenedException("serial port not opened");
                                //Create CRC
                                sendData.crc = ModbusPoll.calculateCRC(data, Convert.ToUInt16(data.Length - 8), 6);
                                byteData = BitConverter.GetBytes((int)sendData.crc);
                                data[data.Length - 2] = byteData[0];
                                data[data.Length - 1] = byteData[1];
                                serialport.Write(data, 6, data.Length - 6);

                                byte[] debugData = new byte[data.Length - 6];
                                Array.Copy(data, 6, debugData, 0, data.Length - 6);
                                if (debug) StoreLogData.Instance.Store("Send Serial-Data: " + BitConverter.ToString(debugData), System.DateTime.Now);
                                if (SendDataChanged != null)
                                {
                                    SendDataChanged(debugData);
                                }

                            }
                            break;
                        case ModbusType.ModbusUDP:
                            {
                                //UdpClient udpClient = new UdpClient();
                                IPEndPoint endPoint = new IPEndPoint(ipAddressIn, portIn);
                                udpClient.Send(data, data.Length, endPoint);
                                if (SendDataChanged != null)
                                {
                                    SendDataChanged(data);
                                }

                            }
                            break;
                        case ModbusType.ModbusTCP:
                            {
                                stream.Write(data, 0, data.Length);
                                if (debug) StoreLogData.Instance.Store("Send Data: " + BitConverter.ToString(data), System.DateTime.Now);
                                if (SendDataChanged != null)
                                {
                                    SendDataChanged(data);
                                }
                            }
                            break;
                    }
                }
                catch (Exception) { }
                if (HoldingRegistersChanged != null)
                    HoldingRegistersChanged(receiveData.startingAdress, 1);
            }
        }

        private void WriteMultipleCoils(ModbusProtocol receiveData, ModbusProtocol sendData, NetworkStream stream, int portIn, IPAddress ipAddressIn)
        {
            sendData.response = true;

            sendData.transactionIdentifier = receiveData.transactionIdentifier;
            sendData.protocolIdentifier = receiveData.protocolIdentifier;

            sendData.unitIdentifier = this.unitIdentifier;
            sendData.functionCode = receiveData.functionCode;
            sendData.startingAdress = receiveData.startingAdress;
            sendData.quantity = receiveData.quantity;

            if ((receiveData.quantity == 0x0000) | (receiveData.quantity > 0x07B0))  //Invalid Quantity
            {
                sendData.errorCode = (byte)(receiveData.functionCode + 0x80);
                sendData.exceptionCode = 3;
            }
            if ((((int)receiveData.startingAdress + (int)receiveData.quantity) > 65535) | (receiveData.startingAdress < 0))    //Invalid Starting adress or Starting address + quantity
            {
                sendData.errorCode = (byte)(receiveData.functionCode + 0x80);
                sendData.exceptionCode = 2;
            }
            if (sendData.exceptionCode == 0)
            {
                lock (lockCoils)
                    for (int i = 0; i < receiveData.quantity; i++)
                    {
                        int shift = i % 16;
                        /*                if ((i == receiveData.quantity - 1) & (receiveData.quantity % 2 != 0))
                                        {
                                            if (shift < 8)
                                                shift = shift + 8;
                                            else
                                                shift = shift - 8;
                                        }*/
                        int mask = 0x1;
                        mask = mask << (shift);
                        if ((receiveData.receiveCoilValues[i / 16] & (ushort)mask) == 0)

                            coils[receiveData.startingAdress + i] = false;
                        else

                            coils[receiveData.startingAdress + i] = true;

                    }
            }
            if (sendData.exceptionCode > 0)
                sendData.length = 0x03;
            else
                sendData.length = 0x06;
            if (true)
            {
                Byte[] data;
                if (sendData.exceptionCode > 0)
                    data = new byte[9 + 2 * Convert.ToInt32(modbusType == ModbusType.ModbusRTU)];
                else
                    data = new byte[12 + 2 * Convert.ToInt32(modbusType == ModbusType.ModbusRTU)];

                Byte[] byteData = new byte[2];
                sendData.length = (byte)(data.Length - 6);

                //Send Transaction identifier
                byteData = BitConverter.GetBytes((int)sendData.transactionIdentifier);
                data[0] = byteData[1];
                data[1] = byteData[0];

                //Send Protocol identifier
                byteData = BitConverter.GetBytes((int)sendData.protocolIdentifier);
                data[2] = byteData[1];
                data[3] = byteData[0];

                //Send length
                byteData = BitConverter.GetBytes((int)sendData.length);
                data[4] = byteData[1];
                data[5] = byteData[0];

                //Unit Identifier
                data[6] = sendData.unitIdentifier;

                //Function Code
                data[7] = sendData.functionCode;



                if (sendData.exceptionCode > 0)
                {
                    data[7] = sendData.errorCode;
                    data[8] = sendData.exceptionCode;
                    sendData.sendRegisterValues = null;
                }
                else
                {
                    byteData = BitConverter.GetBytes((int)receiveData.startingAdress);
                    data[8] = byteData[1];
                    data[9] = byteData[0];
                    byteData = BitConverter.GetBytes((int)receiveData.quantity);
                    data[10] = byteData[1];
                    data[11] = byteData[0];
                }


                try
                {
                    switch (modbusType)
                    {
                        case ModbusType.ModbusRTU:
                            {
                                if (!serialport.IsOpen)
                                    throw new EasyModbus.Exceptions.SerialPortNotOpenedException("serial port not opened");
                                //Create CRC
                                sendData.crc = ModbusPoll.calculateCRC(data, Convert.ToUInt16(data.Length - 8), 6);
                                byteData = BitConverter.GetBytes((int)sendData.crc);
                                data[data.Length - 2] = byteData[0];
                                data[data.Length - 1] = byteData[1];
                                serialport.Write(data, 6, data.Length - 6);

                                byte[] debugData = new byte[data.Length - 6];
                                Array.Copy(data, 6, debugData, 0, data.Length - 6);
                                if (debug) StoreLogData.Instance.Store("Send Serial-Data: " + BitConverter.ToString(debugData), System.DateTime.Now);
                                if (SendDataChanged != null)
                                {
                                    SendDataChanged(debugData);
                                }

                            }
                            break;
                        case ModbusType.ModbusUDP:
                            {
                                //UdpClient udpClient = new UdpClient();
                                IPEndPoint endPoint = new IPEndPoint(ipAddressIn, portIn);
                                udpClient.Send(data, data.Length, endPoint);
                                if (SendDataChanged != null)
                                {
                                    SendDataChanged(data);
                                }

                            }
                            break;
                        case ModbusType.ModbusTCP:
                            {
                                stream.Write(data, 0, data.Length);
                                if (debug) StoreLogData.Instance.Store("Send Data: " + BitConverter.ToString(data), System.DateTime.Now);
                                if (SendDataChanged != null)
                                {
                                    SendDataChanged(data);
                                }
                            }
                            break;
                    }
                }
                catch (Exception) { }
                if (CoilsChanged != null)
                    CoilsChanged(receiveData.startingAdress, receiveData.quantity);
            }
        }

        private void WriteMultipleRegisters(ModbusProtocol receiveData, ModbusProtocol sendData, NetworkStream stream, int portIn, IPAddress ipAddressIn)
        {
            sendData.response = true;

            sendData.transactionIdentifier = receiveData.transactionIdentifier;
            sendData.protocolIdentifier = receiveData.protocolIdentifier;

            sendData.unitIdentifier = this.unitIdentifier;
            sendData.functionCode = receiveData.functionCode;
            sendData.startingAdress = receiveData.startingAdress;
            sendData.quantity = receiveData.quantity;

            if ((receiveData.quantity == 0x0000) | (receiveData.quantity > 0x07B0))  //Invalid Quantity
            {
                sendData.errorCode = (byte)(receiveData.functionCode + 0x80);
                sendData.exceptionCode = 3;
            }
            if ((((int)receiveData.startingAdress + (int)receiveData.quantity) > 65535) | (receiveData.startingAdress < 0))   //Invalid Starting adress or Starting address + quantity
            {
                sendData.errorCode = (byte)(receiveData.functionCode + 0x80);
                sendData.exceptionCode = 2;
            }
            if (sendData.exceptionCode == 0)
            {
                lock (lockHoldingRegisters)
                    for (int i = 0; i < receiveData.quantity; i++)
                    {
                        holdingRegisters[receiveData.startingAdress + i] = unchecked((short)receiveData.receiveRegisterValues[i]);
                    }
            }
            if (sendData.exceptionCode > 0)
                sendData.length = 0x03;
            else
                sendData.length = 0x06;
            if (true)
            {
                Byte[] data;
                if (sendData.exceptionCode > 0)
                    data = new byte[9 + 2 * Convert.ToInt32(modbusType == ModbusType.ModbusRTU)];
                else
                    data = new byte[12 + 2 * Convert.ToInt32(modbusType == ModbusType.ModbusRTU)];

                Byte[] byteData = new byte[2];
                sendData.length = (byte)(data.Length - 6);

                //Send Transaction identifier
                byteData = BitConverter.GetBytes((int)sendData.transactionIdentifier);
                data[0] = byteData[1];
                data[1] = byteData[0];

                //Send Protocol identifier
                byteData = BitConverter.GetBytes((int)sendData.protocolIdentifier);
                data[2] = byteData[1];
                data[3] = byteData[0];

                //Send length
                byteData = BitConverter.GetBytes((int)sendData.length);
                data[4] = byteData[1];
                data[5] = byteData[0];

                //Unit Identifier
                data[6] = sendData.unitIdentifier;

                //Function Code
                data[7] = sendData.functionCode;



                if (sendData.exceptionCode > 0)
                {
                    data[7] = sendData.errorCode;
                    data[8] = sendData.exceptionCode;
                    sendData.sendRegisterValues = null;
                }
                else
                {
                    byteData = BitConverter.GetBytes((int)receiveData.startingAdress);
                    data[8] = byteData[1];
                    data[9] = byteData[0];
                    byteData = BitConverter.GetBytes((int)receiveData.quantity);
                    data[10] = byteData[1];
                    data[11] = byteData[0];
                }


                try
                {
                    switch (modbusType)
                    {
                        case ModbusType.ModbusRTU:
                            {
                                if (!serialport.IsOpen)
                                    throw new EasyModbus.Exceptions.SerialPortNotOpenedException("serial port not opened");
                                //Create CRC
                                sendData.crc = ModbusPoll.calculateCRC(data, Convert.ToUInt16(data.Length - 8), 6);
                                byteData = BitConverter.GetBytes((int)sendData.crc);
                                data[data.Length - 2] = byteData[0];
                                data[data.Length - 1] = byteData[1];
                                serialport.Write(data, 6, data.Length - 6);

                                byte[] debugData = new byte[data.Length - 6];
                                Array.Copy(data, 6, debugData, 0, data.Length - 6);
                                if (debug) StoreLogData.Instance.Store("Send Serial-Data: " + BitConverter.ToString(debugData), System.DateTime.Now);
                                if (SendDataChanged != null)
                                {
                                    SendDataChanged(debugData);
                                }

                            }
                            break;
                        case ModbusType.ModbusUDP:
                            {
                                //UdpClient udpClient = new UdpClient();
                                IPEndPoint endPoint = new IPEndPoint(ipAddressIn, portIn);
                                udpClient.Send(data, data.Length, endPoint);
                                if (SendDataChanged != null)
                                {
                                    SendDataChanged(data);
                                }

                            }
                            break;
                        case ModbusType.ModbusTCP:
                            {
                                stream.Write(data, 0, data.Length);
                                if (debug) StoreLogData.Instance.Store("Send Data: " + BitConverter.ToString(data), System.DateTime.Now);
                                if (SendDataChanged != null)
                                {
                                    SendDataChanged(data);
                                }
                            }
                            break;
                    }
                }
                catch (Exception) { }
                if (HoldingRegistersChanged != null)
                    HoldingRegistersChanged(receiveData.startingAdress, receiveData.quantity);
            }
        }

        private void ReadWriteMultipleRegisters(ModbusProtocol receiveData, ModbusProtocol sendData, NetworkStream stream, int portIn, IPAddress ipAddressIn)
        {
            sendData.response = true;

            sendData.transactionIdentifier = receiveData.transactionIdentifier;
            sendData.protocolIdentifier = receiveData.protocolIdentifier;

            sendData.unitIdentifier = this.unitIdentifier;
            sendData.functionCode = receiveData.functionCode;


            if ((receiveData.quantityRead < 0x0001) | (receiveData.quantityRead > 0x007D) | (receiveData.quantityWrite < 0x0001) | (receiveData.quantityWrite > 0x0079) | (receiveData.byteCount != (receiveData.quantityWrite * 2)))  //Invalid Quantity
            {
                sendData.errorCode = (byte)(receiveData.functionCode + 0x80);
                sendData.exceptionCode = 3;
            }
            if ((((int)receiveData.startingAddressRead + 1 + (int)receiveData.quantityRead) > 65535) | (((int)receiveData.startingAddressWrite + 1 + (int)receiveData.quantityWrite) > 65535) | (receiveData.quantityWrite < 0) | (receiveData.quantityRead < 0))    //Invalid Starting adress or Starting address + quantity
            {
                sendData.errorCode = (byte)(receiveData.functionCode + 0x80);
                sendData.exceptionCode = 2;
            }
            if (sendData.exceptionCode == 0)
            {
                sendData.sendRegisterValues = new Int16[receiveData.quantityRead];
                lock (lockHoldingRegisters)
                    Buffer.BlockCopy(holdingRegisters.localArray, receiveData.startingAddressRead * 2 + 2, sendData.sendRegisterValues, 0, receiveData.quantityRead * 2);

                lock (holdingRegisters)
                    for (int i = 0; i < receiveData.quantityWrite; i++)
                    {
                        holdingRegisters[receiveData.startingAddressWrite + i + 1] = unchecked((short)receiveData.receiveRegisterValues[i]);
                    }
                sendData.byteCount = (byte)(2 * receiveData.quantityRead);
            }
            if (sendData.exceptionCode > 0)
                sendData.length = 0x03;
            else
                sendData.length = Convert.ToUInt16(3 + 2 * receiveData.quantityRead);
            if (true)
            {
                Byte[] data;
                if (sendData.exceptionCode > 0)
                    data = new byte[9 + 2 * Convert.ToInt32(modbusType == ModbusType.ModbusRTU)];
                else
                    data = new byte[9 + sendData.byteCount + 2 * Convert.ToInt32(modbusType == ModbusType.ModbusRTU)];

                Byte[] byteData = new byte[2];

                //Send Transaction identifier
                byteData = BitConverter.GetBytes((int)sendData.transactionIdentifier);
                data[0] = byteData[1];
                data[1] = byteData[0];

                //Send Protocol identifier
                byteData = BitConverter.GetBytes((int)sendData.protocolIdentifier);
                data[2] = byteData[1];
                data[3] = byteData[0];

                //Send length
                byteData = BitConverter.GetBytes((int)sendData.length);
                data[4] = byteData[1];
                data[5] = byteData[0];

                //Unit Identifier
                data[6] = sendData.unitIdentifier;

                //Function Code
                data[7] = sendData.functionCode;

                //ByteCount
                data[8] = sendData.byteCount;


                if (sendData.exceptionCode > 0)
                {
                    data[7] = sendData.errorCode;
                    data[8] = sendData.exceptionCode;
                    sendData.sendRegisterValues = null;
                }
                else
                {
                    if (sendData.sendRegisterValues != null)
                        for (int i = 0; i < (sendData.byteCount / 2); i++)
                        {
                            byteData = BitConverter.GetBytes((Int16)sendData.sendRegisterValues[i]);
                            data[9 + i * 2] = byteData[1];
                            data[10 + i * 2] = byteData[0];
                        }

                }


                try
                {
                    switch (modbusType)
                    {
                        case ModbusType.ModbusRTU:
                            {
                                if (!serialport.IsOpen)
                                    throw new EasyModbus.Exceptions.SerialPortNotOpenedException("serial port not opened");
                                //Create CRC
                                sendData.crc = ModbusPoll.calculateCRC(data, Convert.ToUInt16(data.Length - 8), 6);
                                byteData = BitConverter.GetBytes((int)sendData.crc);
                                data[data.Length - 2] = byteData[0];
                                data[data.Length - 1] = byteData[1];
                                serialport.Write(data, 6, data.Length - 6);
                                byte[] debugData = new byte[data.Length - 6];
                                Array.Copy(data, 6, debugData, 0, data.Length - 6);
                                if (debug) StoreLogData.Instance.Store("Send Serial-Data: " + BitConverter.ToString(debugData), System.DateTime.Now);
                                if (SendDataChanged != null)
                                {
                                    SendDataChanged(debugData);
                                }

                            }
                            break;
                        case ModbusType.ModbusUDP:
                            {
                                //UdpClient udpClient = new UdpClient();
                                IPEndPoint endPoint = new IPEndPoint(ipAddressIn, portIn);
                                udpClient.Send(data, data.Length, endPoint);
                                if (SendDataChanged != null)
                                {
                                    SendDataChanged(data);
                                }

                            }
                            break;
                        case ModbusType.ModbusTCP:
                            {
                                stream.Write(data, 0, data.Length);
                                if (debug) StoreLogData.Instance.Store("Send Data: " + BitConverter.ToString(data), System.DateTime.Now);
                                if (SendDataChanged != null)
                                {
                                    SendDataChanged(data);
                                }
                            }
                            break;
                    }
                }
                catch (Exception) { }
                if (HoldingRegistersChanged != null)
                    HoldingRegistersChanged(receiveData.startingAddressWrite + 1, receiveData.quantityWrite);
            }
        }

        private void sendException(int errorCode, int exceptionCode, ModbusProtocol receiveData, ModbusProtocol sendData, NetworkStream stream, int portIn, IPAddress ipAddressIn)
        {
            sendData.response = true;

            sendData.transactionIdentifier = receiveData.transactionIdentifier;
            sendData.protocolIdentifier = receiveData.protocolIdentifier;

            sendData.unitIdentifier = receiveData.unitIdentifier;
            sendData.errorCode = (byte)errorCode;
            sendData.exceptionCode = (byte)exceptionCode;

            if (sendData.exceptionCode > 0)
                sendData.length = 0x03;
            else
                sendData.length = (ushort)(0x03 + sendData.byteCount);

            if (true)
            {
                Byte[] data;
                if (sendData.exceptionCode > 0)
                    data = new byte[9 + 2 * Convert.ToInt32(modbusType == ModbusType.ModbusRTU)];
                else
                    data = new byte[9 + sendData.byteCount + 2 * Convert.ToInt32(modbusType == ModbusType.ModbusRTU)];
                Byte[] byteData = new byte[2];
                sendData.length = (byte)(data.Length - 6);

                //Send Transaction identifier
                byteData = BitConverter.GetBytes((int)sendData.transactionIdentifier);
                data[0] = byteData[1];
                data[1] = byteData[0];

                //Send Protocol identifier
                byteData = BitConverter.GetBytes((int)sendData.protocolIdentifier);
                data[2] = byteData[1];
                data[3] = byteData[0];

                //Send length
                byteData = BitConverter.GetBytes((int)sendData.length);
                data[4] = byteData[1];
                data[5] = byteData[0];

                //Unit Identifier
                data[6] = sendData.unitIdentifier;


                data[7] = sendData.errorCode;
                data[8] = sendData.exceptionCode;


                try
                {
                    switch (modbusType)
                    {
                        case ModbusType.ModbusRTU:
                            {
                                if (!serialport.IsOpen)
                                    throw new EasyModbus.Exceptions.SerialPortNotOpenedException("serial port not opened");
                                //Create CRC
                                sendData.crc = ModbusPoll.calculateCRC(data, Convert.ToUInt16(data.Length - 8), 6);
                                byteData = BitConverter.GetBytes((int)sendData.crc);
                                data[data.Length - 2] = byteData[0];
                                data[data.Length - 1] = byteData[1];
                                serialport.Write(data, 6, data.Length - 6);

                                byte[] debugData = new byte[data.Length - 6];
                                Array.Copy(data, 6, debugData, 0, data.Length - 6);
                                if (debug) StoreLogData.Instance.Store("Send Serial-Data: " + BitConverter.ToString(debugData), System.DateTime.Now);
                                if (SendDataChanged != null)
                                {
                                    SendDataChanged(debugData);
                                }
                            }
                            break;
                        case ModbusType.ModbusUDP:
                            {
                                //UdpClient udpClient = new UdpClient();
                                IPEndPoint endPoint = new IPEndPoint(ipAddressIn, portIn);
                                udpClient.Send(data, data.Length, endPoint);
                                if (SendDataChanged != null)
                                {
                                    SendDataChanged(data);
                                }

                            }
                            break;
                        case ModbusType.ModbusTCP:
                            {
                                stream.Write(data, 0, data.Length);
                                if (debug) StoreLogData.Instance.Store("Send Data: " + BitConverter.ToString(data), System.DateTime.Now);
                                if (SendDataChanged != null)
                                {
                                    SendDataChanged(data);
                                }
                            }
                            break;
                    }
                }
                catch (Exception) { }
            }
        }

        private void CreateLogData(ModbusProtocol receiveData, ModbusProtocol sendData)
        {
            for (int i = 0; i < 98; i++)
            {
                modbusLogData[99 - i] = modbusLogData[99 - i - 2];

            }
            modbusLogData[0] = receiveData;
            modbusLogData[1] = sendData;

        }

        /// <summary>
        /// Gets or Sets the connection Timeout in case of ModbusTCP connection
        /// </summary>
        public int ConnectionTimeout
        {
            get
            {
                return connectTimeout;
            }
            set
            {
                connectTimeout = value;
            }
        }


        public int NumberOfConnections
        {
            get
            {
                return numberOfConnections;
            }
        }

        public ModbusProtocol[] ModbusLogData
        {
            get
            {
                return modbusLogData;
            }
        }

        public int Port
        {
            get
            {
                return port;
            }
            set
            {
                port = value;


            }
        }

        //[DescriptionAttribute("Activate Modbus UDP; Disable Modbus TCP")]
        //[CategoryAttribute("ModbusProperties")]
        public ModbusType ModbusTypeSelection
        {
            get
            {
                return modbusType;
            }
            set
            {
                modbusType = value;
            }
        }

        public int Baudrate
        {
            get
            {
                return baudrate;
            }
            set
            {
                baudrate = value;
            }
        }

        public System.IO.Ports.Parity Parity
        {
            get
            {
                return parity;
            }
            set
            {
                parity = value;
            }
        }

        public System.IO.Ports.StopBits StopBits
        {
            get
            {
                return stopBits;
            }
            set
            {
                stopBits = value;
            }
        }

        public string SerialPort
        {
            get
            {
                return serialPort;
            }
            set
            {
                serialPort = value;
                //if (serialPort != null)
                //    serialFlag = true;
                //else
                //    serialFlag = false;
            }
        }

        public byte UnitIdentifier
        {
            get
            {
                return unitIdentifier;
            }
            set
            {
                unitIdentifier = value;
            }
        }




        /// <summary>
        /// Gets or Sets the Filename for the LogFile
        /// </summary>
        public string LogFileFilename
        {
            get
            {
                return StoreLogData.Instance.Filename;
            }
            set
            {
                StoreLogData.Instance.Filename = value;
                if (StoreLogData.Instance.Filename != null)
                    debug = true;
                else
                    debug = false;
            }
        }




        public class HoldingRegisters
        {
            public Int16[] localArray = new Int16[65535];
            ModbusClient modbusServer;

            public HoldingRegisters(EasyModbus.ModbusClient modbusServer)
            {
                this.modbusServer = modbusServer;
            }

            public Int16 this[int x]
            {
                get { return this.localArray[x]; }
                set
                {
                    this.localArray[x] = value;

                }
            }
        }

        public class InputRegisters
        {
            public Int16[] localArray = new Int16[65535];
            ModbusClient modbusServer;

            public InputRegisters(EasyModbus.ModbusClient modbusServer)
            {
                this.modbusServer = modbusServer;
            }

            public Int16 this[int x]
            {
                get { return this.localArray[x]; }
                set
                {
                    this.localArray[x] = value;

                }
            }
        }

        public class Coils
        {
            public bool[] localArray = new bool[65535];
            ModbusClient modbusServer;

            public Coils(EasyModbus.ModbusClient modbusServer)
            {
                this.modbusServer = modbusServer;
            }

            public bool this[int x]
            {
                get { return this.localArray[x]; }
                set
                {
                    this.localArray[x] = value;

                }
            }
        }

        public class DiscreteInputs
        {
            public bool[] localArray = new bool[65535];
            ModbusClient modbusServer;

            public DiscreteInputs(EasyModbus.ModbusClient modbusServer)
            {
                this.modbusServer = modbusServer;
            }

            public bool this[int x]
            {
                get { return this.localArray[x]; }
                set
                {
                    this.localArray[x] = value;

                }
            }


        }
    }
}
